Skip to content

Persist search sheet state across close/reopen#5534

Draft
FadhlanR wants to merge 5 commits into
mainfrom
cs-12109-host-persist-search-sheet-state-query-filters-view-results
Draft

Persist search sheet state across close/reopen#5534
FadhlanR wants to merge 5 commits into
mainfrom
cs-12109-host-persist-search-sheet-state-query-filters-view-results

Conversation

@FadhlanR

Copy link
Copy Markdown
Contributor

Resolves CS-12109.

Problem

Closing the operator-mode search sheet discards the entire search — reopening starts from a blank slate. The @mode gate at search-sheet/index.gts destroys the whole SearchPanel → PanelContent → SearchResults → SearchEntriesResource subtree on close, dropping the query, filters, sort, view, pagination, and results.

Approach

A new in-memory, session-scoped SearchSheetState service holds the sheet's state (query text, selected types/realms, sort, view toggle, pagination) plus a snapshot of the last results.

  • The @mode-gated subtree is still destroyed on close, so there is no background work while the sheet is closed (the search resource and its realm subscriptions tear down).
  • On reopen the subtree remounts, rehydrates from the service, redisplays the cached results snapshot immediately (no blank flash), and re-runs the query to refresh — so newly-created cards show up. Stale-while-revalidate.
  • Persistence is opt-in via a @persist flag passed only by the search sheet, so the card choosers (which render their own SearchPanel instances) keep their own clean, per-invocation state.
  • The service registers with the reset service, so logout / realm reset clears it. A page reload also clears it (in-memory only, no localStorage).
  • resetState now runs only on explicit Cancel / Escape — removed from plain close/blur and from result-select, so those keep the search. Reopening a persisted search opens straight to the results view.

Files

  • services/search-sheet-state.ts (new) — the in-memory store + results snapshot, reset-registered.
  • search-sheet/index.gts — state backed by the service; reset only on explicit cancel.
  • card-search/panel.gts — additive initialActiveSort / onSortChange / persist args (choosers unaffected).
  • card-search/panel-content.gts — view + pagination behind @persist; main-results snapshot capture/redisplay.
  • operator-mode/submode-layout.gts — reopen to the results view when a search is persisted.

Verification

lint:types, lint:js, and lint:hbs pass in packages/host (with boxel-icons + boxel-ui built).

Remaining

  • Host acceptance tests: persist across close/reopen; result-select keeps the search; Cancel/Escape resets; choosers stay clean; in-memory (reset/reload) clears. Draft opened ahead of these per request.
  • Manual verification in the dev stack (snapshot → live handoff on reopen).

🤖 Generated with Claude Code

@FadhlanR
FadhlanR force-pushed the cs-12109-host-persist-search-sheet-state-query-filters-view-results branch from 6474fb0 to 48ab0d6 Compare July 17, 2026 07:31
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files      1 suites   2h 48m 39s ⏱️
3 564 tests 3 549 ✅ 15 💤 0 ❌
3 583 runs  3 568 ✅ 15 💤 0 ❌

Results for commit 37a7867.

Realm Server Test Results

    1 files      1 suites   16m 53s ⏱️
1 883 tests 1 883 ✅ 0 💤 0 ❌
1 962 runs  1 962 ✅ 0 💤 0 ❌

Results for commit 37a7867.

FadhlanR and others added 4 commits July 20, 2026 19:49
Add an in-memory, session-scoped SearchSheetState service that holds the
operator-mode search sheet's query, selected types/realms, sort, view, and
pagination, plus a snapshot of the last results. The sheet's @mode-gated
subtree is still destroyed on close (no background work while closed); on
reopen it rehydrates from the service, redisplays the cached results snapshot
immediately, and re-runs the query to refresh.

Persistence is opt-in via a `@persist` flag passed only by the search sheet,
so the card choosers keep their own ephemeral state. The service registers
with the reset service, so logout/realm reset clears it; a page reload also
clears it (in-memory, no localStorage).

resetState now runs only on explicit Cancel/Escape (removed from plain
close/blur and result-select), and reopening a persisted search opens straight
to the results view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The search sheet's result snapshot and the search resource each wrote
tracked state during the render that consumed it, tripping Glimmer's
mutate-after-consume assertion and failing host tests as a global error.

- Capture the main-results snapshot in a microtask instead of during the
  modifier's render, and key it to the current wire query so a reopen only
  redisplays results for the same search (never stale rows under a different
  term, and an idle sheet never clobbers a snapshot). Extract the
  snapshot-vs-live decision into a pure, unit-tested helper.
- Start SearchEntriesResource's search a microtask after modify so the
  task's isRunning write lands outside the consuming render — the case that
  bites when a SearchResults mounts with a query already set (the sheet
  reopening restored, or a chooser rendering recents). Load tracking stays
  synchronous via a Deferred so prerender readiness is unaffected.
- Update the reopen expectation in the interact-submode acceptance test to
  the persisted behavior, and add acceptance + unit coverage for persist,
  Cancel/Escape reset, empty-then-close, and service reset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reopening the operator-mode search sheet with an unchanged query now
seeds the freshly-mounted search resource from the retained snapshot and
skips the fetch, so the prior results show immediately with no re-run and
no "Searching…" flash. A one-shot guard keeps the seed from re-applying,
so any query change still runs a fresh search. The results-list scroll
offset is captured on scroll and restored on reopen (session-only).

The seeded resource holds the rows directly, so the display-time
resolveMainResults snapshot path is removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The search sheet now keeps its query and filters on a plain close (click
outside); only an explicit Cancel or Escape clears. Update the two
operator-mode UI tests that asserted the old reset-on-blur behavior:

- realm filter: selecting a realm then clicking outside and reopening
  keeps the realm checked.
- type filter: a search term plus a type selection survive click-outside,
  so reopening returns to the results view with both still applied.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FadhlanR
FadhlanR force-pushed the cs-12109-host-persist-search-sheet-state-query-filters-view-results branch from 7a7cbc7 to 04c73c6 Compare July 20, 2026 12:50
The code-mode playground autogenerates a blank instance when it sees a
search that has settled with no results. Starting the search resource's
task a microtask after modify() (needed so a SearchResults mounting with a
query already set doesn't flip isRunning mid-render and trip Glimmer's
backtracking assertion) left a window where a just-set query read as
settled-with-no-results — so the playground autogenerated "Untitled"
instances, breaking the playground / spec / AI-assistant suites.

Keep the deferred start, but have isLoading also report loading across
that gap: it's true whenever a query is set and no run has completed yet,
read from the untracked #previousQuery / hasCompletedFullRun fields so
nothing tracked is mutated mid-render. The seed branch marks the run
complete synchronously, so a seeded reopen still presents as settled with
no flash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FadhlanR
FadhlanR force-pushed the cs-12109-host-persist-search-sheet-state-query-filters-view-results branch from 4b75873 to 37a7867 Compare July 20, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant